package DataAccess;

import java.util.List;
import java.util.Set;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import domain.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.logging.Level;
import java.util.logging.Logger;

//DAO class
@Stateless
public class DataAccess implements DataAccessInterface {

        private boolean i = true;
        @PersistenceContext
        private EntityManager em;

        @Override
        public void deleteReference(String email, int id) {
                Reference ref = (Reference) em.createQuery("Select reference From Reference reference Where reference.id = :id")
                        .setParameter("id", id)
                        .getSingleResult();

                AccUser user = (AccUser) em.createQuery("Select accuser From AccUser accuser Where accuser.email = :name")
                        .setParameter("name", email)
                        .getSingleResult();

                try {

                        user.deleteReference(id);

                        Publisher pub = (Publisher) em.createQuery("Select publisher From Publisher publisher Where publisher.id = :name")
                                .setParameter("name", ref.getPublisher().getId())
                                .getSingleResult();
                        pub.deleteReference(id);

                        //we have to unlink autors before delete ref
                        Set<Author> aaa = ref.getAuthors();
                        for (Author aa : aaa) {
                                Author m1 = (Author) em.createQuery("SELECT author FROM Author author WHERE author.id = :email")
                                        .setParameter("email", aa.getId())
                                        .getSingleResult();
                                m1.deleteReference(ref);
                        }

                        Set<Translator> ttt = ref.getTranslators();
                        for (Translator tt : ttt) {
                                Translator Tauthor = (Translator) em.createQuery("Select translator From Translator translator Where translator.id = :name")
                                        .setParameter("name", tt.getId())
                                        .getSingleResult();
                                Tauthor.deleteReference(ref);
                        }

                        em.remove(ref);
                        em.remove(pub);

                        em.flush();

                } catch (Exception e) {
                        System.out.println("this is what:" + e);
                }
        }

        @Override
        public List<AccUser> findAll() {
                // User newGPU = new User();
                //em.persist(newGPU);
                Query q = em.createQuery("Select accuser From AccUser accuser");
                return q.getResultList();

        }

        @Override
        public AccUser findByEmail(String email) {

                try {
                        Query q = em.createQuery("Select accuser From AccUser accuser Where accuser.email = :name");
                        q.setParameter("name", email);
                        return (AccUser) q.getSingleResult();
                } catch (Exception e) {
                        return null;
                }
        }

        @Override
        public void addUser(AccUser newGPU) {
                // TODO Auto-generated method stub
                //em.persist(newGPU);
                DateFormat df = new SimpleDateFormat("dd/MM/yy hh:mm:ss");
                String formattedDate = df.format(new Date());
                String password = newGPU.getPassword() + formattedDate;

                /////////////////////////////http://www.mkyong.com/java/java-sha-hashing-example/
                MessageDigest md;
                try {
                        md = MessageDigest.getInstance("SHA-256");

                        md.update(password.getBytes());

                        byte byteData[] = md.digest();

                        //convert the byte to hex format method 1
                        StringBuffer sb = new StringBuffer();
                        for (int i = 0; i < byteData.length; i++) {
                                sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
                        }

                        System.out.println("Hex format : " + sb.toString());

                        //convert the byte to hex format method 2
                        StringBuffer hexString = new StringBuffer();
                        for (int i = 0; i < byteData.length; i++) {
                                String hex = Integer.toHexString(0xff & byteData[i]);
                                if (hex.length() == 1) {
                                        hexString.append('0');
                                }
                                hexString.append(hex);
                        }
                        System.out.println("Hex format : " + hexString.toString());

                        newGPU.setSalt(formattedDate);
                        newGPU.setPassword(hexString.toString());
                        em.persist(newGPU);

                } catch (NoSuchAlgorithmException ex) {
                        Logger.getLogger(DataAccess.class.getName()).log(Level.SEVERE, null, ex);
                }

                ////////////////////////////http://www.mkyong.com/java/java-sha-hashing-example/
        }

        public void RemoveGPU(int Mid) {
                AccUser m1 = (AccUser) em.createQuery("SELECT gpu FROM GPU gpu WHERE gpu.id = :id")
                        .setParameter("id", Mid)
                        .getSingleResult();
                try {
                        em.remove(m1);
                } catch (Exception e) {
                        System.out.println(e);
                }

        }

        @Override
        public void addRef(String email, Reference newRef) {
                em.persist(newRef);

                /*AccUser m1 = (AccUser) em.createQuery("SELECT accuser FROM AccUser accuser WHERE accuser.email = :email")
                 .setParameter("email", email)
                 .getSingleResult();
                 m1.addReference(newRef);*/
                //email = "c";
        /*Author m2 = (Author) em.createQuery("SELECT author FROM Author author WHERE author.name = :email")
                 .setParameter("email", email)
                 .getSingleResult();
                 m2.addReference(newRef);*/
                em.flush();
        }

        @Override
        public void addAuthor(String email1, Author newAuthor) {
                em.persist(newAuthor);

                Translator translator = new Translator(newAuthor);
                em.persist(translator);

                Query q = em.createQuery("Select accuser From AccUser accuser Where accuser.email = :name");
                q.setParameter("name", email1);
                AccUser aa = (AccUser) q.getSingleResult();
                aa.addAuthor(newAuthor);

                /*AccUser m1 = (AccUser) em.createQuery("SELECT accuser FROM AccUser accuser WHERE accuser.email = :email")
                 .setParameter("email", email1)
                 .getSingleResult();*/
                //m1.addAuthor(newAuthor);
                em.flush();
        }

        @Override
        public Author searchbByANAME(String name) {
                Author m1 = (Author) em.createQuery("SELECT author FROM Author author WHERE author.name = :email")
                        .setParameter("email", name)
                        .getSingleResult();
                return m1;
        }

        @Override
        public Translator searchbByANAMEtrans(String name) {
                Translator Tauthor = (Translator) em.createQuery("Select translator From Translator translator Where translator.name = :name")
                        .setParameter("name", name)
                        .getSingleResult();
                return Tauthor;
        }

        @Override
        public Author searchbByASURNAME(String surname) {
                Author m1 = (Author) em.createQuery("SELECT author FROM Author author WHERE author.surname = :email")
                        .setParameter("email", surname)
                        .getSingleResult();
                return m1;
        }

        @Override
        public Reference addPublisher(int oldPubID, int RefID, String SelectedReferencePub, String SelectedReferencePubcountry) {
                Reference reference;

                //if -111 ceate else edit PUB addEditPub()
                if (oldPubID == -111) {
                        Publisher p = new Publisher(SelectedReferencePub, SelectedReferencePubcountry);

                        reference = (Reference) em.createQuery("Select reference From Reference reference Where reference.id = :name")
                                .setParameter("name", RefID)
                                .getSingleResult();
                        reference.setPublisher(p);
                        p.addReference(reference);
                        em.persist(p);
                } else {
                        Publisher pp = (Publisher) em.createQuery("Select publisher From Publisher publisher Where publisher.id = :name")
                                .setParameter("name", oldPubID)
                                .getSingleResult();

                        pp.setName(SelectedReferencePub);
                        pp.setCountry(SelectedReferencePubcountry);

                        reference = (Reference) em.createQuery("Select reference From Reference reference Where reference.id = :name")
                                .setParameter("name", RefID)
                                .getSingleResult();
                        reference.setPublisher(pp);// shouldnt be neserery
                }
                return reference;
        }

        @Override
        public void addRefBig(Reference r, String email) {

                em.persist(r);
                //em.persist(p);

                Query q = em.createQuery("Select accuser From AccUser accuser Where accuser.email = :name");
                q.setParameter("name", email);
                AccUser ac = (AccUser) q.getSingleResult();

                /*for (Author test : aa) {
                 Author author = (Author) em.createQuery("Select author From Author author Where author.name = :name")
                 .setParameter("name", test.getName())
                 .getSingleResult();

                 //em.persist(test);
                 //ac.addAuthor(test);
                 r.addAuthor(author);
                 author.addReference(r);
                 }*/

                /*for (Author t : tt) {
                 //em.persist(test);
                 //ac.addAuthor(t);
                 //r.addAuthor(t);
                 Translator Tauthor = (Translator) em.createQuery("Select translator From Translator translator Where translator.name = :name")
                 .setParameter("name", t.getName())
                 .getSingleResult();

                 //em.persist(test);
                 //ac.addAuthor(test);
                 r.addTranslator(Tauthor);
                 Tauthor.addTReference(r);
                 }*/
                ac.addReference(r);
                //r.setPublisher(p);
                //p.addReference(r); //persist   

                em.flush();

        }

        //  true = this user exist
        //  false = this user doesnt exist or this user's email that u trying to chage belows to this user
        @Override
        public boolean UserExist(int id, String email) {
                try {
                        Query q = em.createQuery("Select accuser From AccUser accuser Where accuser.email = :name");
                        q.setParameter("name", email);
                        q.getResultList();
                        AccUser uu = (AccUser) q.getSingleResult();

                        if (id == uu.getId())//this step is there to make sure a user cant change his email if to a other user email
                        {
                                return false;
                        }
                } catch (Exception e) {
                        return false;
                }
                return true;
        }

        @Override



        public void updateUser(int id, String name, String email, String pass) {
                Query q = em.createQuery("Select accuser From AccUser accuser Where accuser.id = :id");
                q.setParameter("id", id);
                AccUser u = (AccUser) q.getSingleResult();

                if (pass.length() == 0) {

                        u.setEmail(email);
                        u.setName(name);

                } else if (pass.length() != 0) {

                        // TODO Auto-generated method stub
                        //em.persist(newGPU);
                        DateFormat df = new SimpleDateFormat("dd/MM/yy hh:mm:ss");
                        String formattedDate = df.format(new Date());
                        String password = pass + formattedDate;

                        /////////////////////////////http://www.mkyong.com/java/java-sha-hashing-example/
                        MessageDigest md;
                        try {
                                md = MessageDigest.getInstance("SHA-256");

                                md.update(password.getBytes());

                                byte byteData[] = md.digest();

                                //convert the byte to hex format method 1
                                StringBuffer sb = new StringBuffer();
                                for (int i = 0; i < byteData.length; i++) {
                                        sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));
                                }

                                System.out.println("Hex format : " + sb.toString());

                                //convert the byte to hex format method 2
                                StringBuffer hexString = new StringBuffer();
                                for (int i = 0; i < byteData.length; i++) {
                                        String hex = Integer.toHexString(0xff & byteData[i]);
                                        if (hex.length() == 1) {
                                                hexString.append('0');
                                        }
                                        hexString.append(hex);
                                }
                                System.out.println("Hex format : " + hexString.toString());

                                u.setSalt(formattedDate);
                                u.setPassword(hexString.toString());

                        } catch (NoSuchAlgorithmException ex) {
                                Logger.getLogger(DataAccess.class.getName()).log(Level.SEVERE, null, ex);
                        }

                        u.setEmail(email);
                        u.setName(name);

                }
        }

        @Override
        public List<Author> getAllAuthors() {
                Query q = em.createQuery("Select author From Author author ORDER BY author.name ASC");
                return q.getResultList();
        }

        @Override
        public List<Author> getallAuthors(int id) {
                Query q = em.createQuery("Select accuser From AccUser accuser Where accuser.id = :id ORDER BY accuser.id, accuser.name ASC");
                q.setParameter("id", id);
                AccUser u = (AccUser) q.getSingleResult();
                return u.getAuthors();
                /*Query q = em.createQuery("Select author From Author author");
                 Set<Author> ll = new HashSet<Author>(q.getResultList());
                 return ll;*/

        }

        @Override
        public void deleteAuthor(String email, int id) {

                Author author = (Author) em.createQuery("Select author From Author author Where author.id = :name")
                        .setParameter("name", id)
                        .getSingleResult();

                Translator Tauthor = (Translator) em.createQuery("Select translator From Translator translator Where translator.id = :name")
                        .setParameter("name", id)
                        .getSingleResult();

                AccUser user = (AccUser) em.createQuery("Select accuser From AccUser accuser Where accuser.email = :name")
                        .setParameter("name", email)
                        .getSingleResult();

                /*Translator Tauthor = (Translator) em.createQuery("Select translator From Translator translator Where translator.id = :name")
                 .setParameter("name", newAuthor.getId())
                 .getSingleResult();  */

                /*Query q = em.createQuery("Select accuser From AccUser accuser Where accuser.email = :name");
                 q.setParameter("name", email);*/
                try {

                        user.deleteAuthor(id);

                        Set<Reference> a = author.getReference();
                        for (Reference aa : a) {
                                Reference reference = (Reference) em.createQuery("Select reference From Reference reference Where reference.id = :name")
                                        .setParameter("name", aa.getId())
                                        .getSingleResult();
                                reference.deleteAuthor(id);
                        }
                        Set<Reference> b = Tauthor.getTreference();
                        for (Reference bb : b) {
                                Reference reference = (Reference) em.createQuery("Select reference From Reference reference Where reference.id = :name")
                                        .setParameter("name", bb.getId())
                                        .getSingleResult();
                                reference.deleteTranslator(id);
                        }

                        em.remove(author);

                        em.remove(Tauthor);
                        /*AccUser a = (AccUser) q.getSingleResult();
                         a.deleteAuthor(id);

                         Query qq = em.createQuery("Select author From Author author Where author.id = :name");
                         q.setParameter("name", id);
                         Author aa = (Author) qq.getSingleResult();*/

                        //em.remove(aa);
                } catch (Exception e) {
                        System.out.println(e);
                }

        }
        /* GPU m1 = (GPU) em.createQuery("SELECT gpu FROM GPU gpu WHERE gpu.id = :id")
         .setParameter("id", Gid)
         .getSingleResult();
         try
         {
         m1.RemoveNote(Mid);
         }
         catch (Exception e)
         {
         System.out.println(e);
         }*/

        @Override
        public void updateAuthor(Author a) {

                Author author = (Author) em.createQuery("Select author From Author author Where author.id = :name")
                        .setParameter("name", a.getId())
                        .getSingleResult();

                author.setName(a.getName());
                author.setSurname(a.getSurname());
                author.setInitials(a.getInitials());
                author.setNames(a.getNames());

        }

        @Override
        public List<Reference> getAllRef(int id) {
                Query q = em.createQuery("Select accuser From AccUser accuser Where accuser.id = :id");
                q.setParameter("id", id);
                AccUser u = (AccUser) q.getSingleResult();
                return u.getRefs();
                /*Query q = em.createQuery("Select author From Author author");
                 Set<Author> ll = new HashSet<Author>(q.getResultList());
                 return ll;*/
        }

        @Override
        public void updateReference(Reference r) {
                Reference reference = (Reference) em.createQuery("Select reference From Reference reference Where reference.id = :name")
                        .setParameter("name", r.getId())
                        .getSingleResult();

                reference.setTitle(r.getTitle());
                reference.setrYear(r.getrYear());
                reference.setType(r.getType());
                reference.setEdition(r.getEdition());
                reference.setCity(r.getCity());

                reference.setDoi(r.getDoi());
                reference.setIsbn10(r.getIsbn10());
                reference.setIsbn13(r.getIsbn13());
                reference.setPages(r.getPages());
                reference.setVolume(r.getVolume());

                reference.setIssue(r.getIssue());
                reference.setAddress(r.getAddress());
                reference.setDate(r.getDate());
                reference.setOther(r.getOther());

                reference.setLanguage(r.getLanguage());
                reference.setSubject(r.getSubject());
        }

        @Override
        public void deletePub(String Pname, String Pcountry, Reference r) {
                Publisher ppp = (Publisher) em.createQuery("Select publisher From Publisher publisher Where publisher.country = :name2")
                        //.setParameter("name", Pname)
                        .setParameter("name2", Pcountry)
                        .getSingleResult();

                Reference reference = (Reference) em.createQuery("Select reference From Reference reference Where reference.id = :name3")
                        .setParameter("name3", r.getId())
                        .getSingleResult();

                /*Query q = em.createQuery("Select publisher_reference From PUBLISHER_REFERENCE publisher_reference"); 
                 List<publisher_reference> pr =  q.getResultList();
        
                 if
                 em.remove(ppp);*/
                // we have a problem here, cant delete PUB, coz might be linked to a other refsomewhere!!!!!!
                //we must ask client must we share pub between user's ref of do a one 2 one relationship between ref and pub
                try {
                        reference.deletePub();
                        em.remove(ppp); //or now i am goung to delete pub  
                } catch (Exception e) {
                        System.out.println(e);
                }
        }

        @Override
        public Reference linkAuthor(Reference selectedReference, Author newAuthor) {
                Reference reference = (Reference) em.createQuery("Select reference From Reference reference Where reference.id = :name3")
                        .setParameter("name3", selectedReference.getId())
                        .getSingleResult();

                Author author = (Author) em.createQuery("Select author From Author author Where author.id = :name")
                        .setParameter("name", newAuthor.getId())
                        .getSingleResult();

                reference.addAuthor(author);
                author.addReference(reference);

                return reference;
        }

        @Override
        public Reference linkTAuthor(Reference selectedReference, Translator newAuthor) {

                Reference reference = (Reference) em.createQuery("Select reference From Reference reference Where reference.id = :name3")
                        .setParameter("name3", selectedReference.getId())
                        .getSingleResult();

                Translator Tauthor = (Translator) em.createQuery("Select translator From Translator translator Where translator.id = :name")
                        .setParameter("name", newAuthor.getId())
                        .getSingleResult();

                reference.addTranslator(Tauthor);
                Tauthor.addTReference(reference);

                return reference;

                //return null;
        }

        @Override
        public Reference unlinkAuthor(Reference selectedReference, Author newAuthor) {
                Reference reference = (Reference) em.createQuery("Select reference From Reference reference Where reference.id = :name3")
                        .setParameter("name3", selectedReference.getId())
                        .getSingleResult();

                Author author = (Author) em.createQuery("Select author From Author author Where author.id = :name")
                        .setParameter("name", newAuthor.getId())
                        .getSingleResult();

                reference.deleteAuthor(author.getId());
                author.deleteReference(reference);

                return reference;
        }

        @Override
        public Reference unlinkTAuthor(Reference selectedReference, Translator selectedTranslator) {
                Reference reference = (Reference) em.createQuery("Select reference From Reference reference Where reference.id = :name3")
                        .setParameter("name3", selectedReference.getId())
                        .getSingleResult();

                Translator Tauthor = (Translator) em.createQuery("Select translator From Translator translator Where translator.id = :name")
                        .setParameter("name", selectedTranslator.getId())
                        .getSingleResult();

                reference.deleteTranslator(Tauthor.getId());
                Tauthor.deleteReference(reference);

                return reference;
        }

        @Override
        public AccUser addAcademicSet(int id, String setName) {
                AccUser user = (AccUser) em.createQuery("Select accuser From AccUser accuser Where accuser.id = :name")
                        .setParameter("name", id)
                        .getSingleResult();

                AcademicSet as = new AcademicSet(setName);

                user.addAcademicSet(as);
                as.setAccUserOwner(user);

                em.persist(as);
                em.flush();

                return user;
        }

        @Override
        public List<AcademicSet> getAllAcademicSets(int id) {
                Query q = em.createQuery("Select accuser From AccUser accuser Where accuser.id = :id");
                q.setParameter("id", id);
                AccUser u = (AccUser) q.getSingleResult();
                return u.getPublications();
                /*Query q = em.createQuery("Select author From Author author");
                 Set<Author> ll = new HashSet<Author>(q.getResultList());
                 return ll;*/
        }

        @Override
        public AcademicSet removeRefToSet(AcademicSet selectedAcademicSet, Reference selectedSetReference) {
                AcademicSet sett = (AcademicSet) em.createQuery("Select academicSet From AcademicSet academicSet Where academicSet.id = :name")
                        .setParameter("name", selectedAcademicSet.getId())
                        .getSingleResult();

                sett.removeReference(selectedSetReference.getId());

                return sett;
        }

        @Override
        public AcademicSet addRefToSet(AcademicSet selectedSet, Reference selectedRef) {
                Reference reference = (Reference) em.createQuery("Select reference From Reference reference Where reference.id = :name3")
                        .setParameter("name3", selectedRef.getId())
                        .getSingleResult();

                AcademicSet setttttttttt = (AcademicSet) em.createQuery("Select academicSet From AcademicSet academicSet Where academicSet.id = :name")
                        .setParameter("name", selectedSet.getId())
                        .getSingleResult();
                if (!setttttttttt.getReferencess().contains(reference)) {
                        setttttttttt.addReference(reference);
                }

                return setttttttttt;

        }

        @Override
        public void deleteSelectedAcademicSet(AcademicSet selectedAcademicSet, AccUser current) {
                AcademicSet sett = (AcademicSet) em.createQuery("Select academicSet From AcademicSet academicSet Where academicSet.id = :name")
                        .setParameter("name", selectedAcademicSet.getId())
                        .getSingleResult();

                AccUser user = (AccUser) em.createQuery("Select accuser From AccUser accuser Where accuser.id = :namee")
                        .setParameter("namee", current.getId())
                        .getSingleResult();

                user.deletePublications(selectedAcademicSet.getId());

                Set<Reference> sr = sett.getReferencess();

                for (Reference rrr : sr) {
                        sett.removeReference(rrr.getId());
                }

                em.remove(sett);
        }

        @Override
        public void renameSelectedAcademicSet(AcademicSet selSet, String selectedAcademicSetName) {
                AcademicSet setttttttttt = (AcademicSet) em.createQuery("Select academicSet From AcademicSet academicSet Where academicSet.id = :name")
                        .setParameter("name", selSet.getId())
                        .getSingleResult();

                setttttttttt.setName(selectedAcademicSetName);
        }

        @Override
        public List<Format> allFormats(int id) {

                Query q = em.createQuery("Select accuser From AccUser accuser Where accuser.id = :id");
                q.setParameter("id", id);
                AccUser u = (AccUser) q.getSingleResult();
                
                 return u.getFormats();
                /*Query q = em.createQuery("Select author From Author author");
                 Set<Author> ll = new HashSet<Author>(q.getResultList());
                 return ll;*/

        }
        
        @Override
        public AccUser addFormat(int l, Format f){
                Query q = em.createQuery("Select accuser From AccUser accuser Where accuser.id = :id");
                q.setParameter("id", l);
                AccUser u = (AccUser) q.getSingleResult();
                
                /*
                Format as = new Format();
                as = f;
                */
                u.addFormat(f); 
                
               
                em.persist(f);  
                em.flush();
                
                return u;
                
        }
        
        
        @Override
        public void deleteSelectedFormat(Format selectedFormat, AccUser current){
            
                Query q = em.createQuery("Select accuser From AccUser accuser Where accuser.id = :id");
                q.setParameter("id", current.getId());
                AccUser u = (AccUser) q.getSingleResult();
        
                u.deleteFormat(selectedFormat.getId());
                em.remove(em.merge(selectedFormat)); 
                em.flush(); 
                
                
        }
        
        @Override
        public void renameSelectedFormat(Format selFOR, String selectedFormatName) {
                /*AcademicSet setttttttttt = (AcademicSet) em.createQuery("Select academicSet From AcademicSet academicSet Where academicSet.id = :name")
                        .setParameter("name", selSet.getId())
                        .getSingleResult();*/

                //setttttttttt.setName(selectedAcademicSetName);
                selFOR.setName(selectedFormatName);
                em.merge(selFOR);
                
        }
        

}
